package com.lingdian.common.util;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ZookeeperTest {
	
	  private static final int SESSION_TIMEOUT = 30000;  
      
	    public static final Logger LOGGER = LoggerFactory.getLogger(ZookeeperTest.class);  
	      
	    private Watcher watcher =  new Watcher() {  
	  
	        public void process(WatchedEvent event) {  
	            LOGGER.info("process : " + event.getType());  
	        }  
	    };  
	    private ZooKeeper zooKeeper;  
	    
	    @Before  
	    public void connect() throws IOException {  
	        zooKeeper  = new ZooKeeper("localhost:2181,localhost:2182", SESSION_TIMEOUT, watcher);  
	    }  
	      
	    /** 
	     *  关闭连接 
	     * <br>------------------------------<br> 
	     */  
	    @After  
	    public void close() {  
	        try {  
	            zooKeeper.close();  
	        } catch (InterruptedException e) {  
	            e.printStackTrace();  
	        }  
	    }  
	    /** 
	     * 创建一个znode  
	     *  1.CreateMode 取值   
	     *  PERSISTENT：持久化，这个目录节点存储的数据不会丢失 
	     *  PERSISTENT_SEQUENTIAL：顺序自动编号的目录节点，这种目录节点会根据当前已近存在的节点数自动加 1，然后返回给客户端已经成功创建的目录节点名； 
	     *  EPHEMERAL：临时目录节点，一旦创建这个节点的客户端与服务器端口也就是 session过期超时，这种节点会被自动删除 
	     *  EPHEMERAL_SEQUENTIAL：临时自动编号节点 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testCreate() {  
	        String result = null;  
	         try {  
	             result = zooKeeper.create("/zk001", "zk001data".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         LOGGER.info("create result : {}", result);  
	     }  
	      
	    /** 
	     * 删除节点  忽略版本 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testDelete() {  
	         try {  
	            zooKeeper.delete("/zk001", -1);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	    }  
	      
	    /** 
	     *   获取数据 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testGetData() {  
	        String result = null;  
	         try {  
	             byte[] bytes = zooKeeper.getData("/zk001", null, null);  
	             result = new String(bytes);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         LOGGER.info("getdata result : {}", result);  
	    }  
	      
	    /** 
	     *   获取数据  设置watch 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testGetDataWatch() {  
	        String result = null;  
	         try {  
	             byte[] bytes = zooKeeper.getData("/zk001", new Watcher() {  
	                public void process(WatchedEvent event) {  
	                    LOGGER.info("testGetDataWatch  watch : {}", event.getType());  
	                }  
	             }, null);  
	             result = new String(bytes);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         LOGGER.info("getdata result : {}", result);  
	           
	         // 触发wacth  NodeDataChanged  
	         try {  
	             zooKeeper.setData("/zk001", "testSetData".getBytes(), -1);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	    }  
	      
	    /** 
	     *    判断节点是否存在 
	     *    设置是否监控这个目录节点，这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testExists() {  
	        Stat stat = null;  
	         try {  
	             stat = zooKeeper.exists("/zk001", false);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         Assert.assertNotNull(stat);  
	         LOGGER.info("exists result : {}", stat.getCzxid());  
	    }  
	      
	    /** 
	     *     设置对应znode下的数据  ,  -1表示匹配所有版本 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testSetData() {  
	        Stat stat = null;  
	         try {  
	             stat = zooKeeper.setData("/zk001", "testSetData".getBytes(), -1);  
	        } catch (Exception e) { 
	        	System.out.println("xxx");
	        	e.printStackTrace();
//	             LOGGER.error(e.getMessage());  
//	             Assert.fail();  
	        }  
	         Assert.assertNotNull(stat);  
	         LOGGER.info("exists result : {}", stat.getVersion());    
	    }  
	      
	    /** 
	     *    判断节点是否存在,  
	     *    设置是否监控这个目录节点，这里的 watcher 是在创建 ZooKeeper实例时指定的 watcher 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testExistsWatch1() {  
	        Stat stat = null;  
	         try {  
	             stat = zooKeeper.exists("/zk001", true);  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         Assert.assertNotNull(stat);  
	           
	         try {  
	            zooKeeper.delete("/zk001", -1);  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	        }  
	    }  
	      
	    /** 
	     *    判断节点是否存在,  
	     *    设置监控这个目录节点的 Watcher 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testExistsWatch2() {  
	        Stat stat = null;  
	         try {  
	             stat = zooKeeper.exists("/zk002", new Watcher() {  
	                @Override  
	                public void process(WatchedEvent event) {  
	                    LOGGER.info("testExistsWatch2  watch : {}", event.getType());  
	                }  
	             });  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	         Assert.assertNotNull(stat);  
	           
	         // 触发watch 中的process方法   NodeDataChanged  
	         try {  
	            zooKeeper.setData("/zk002", "testExistsWatch2".getBytes(), -1);  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	        }  
	           
	         // 不会触发watch 只会触发一次  
	         try {  
	            zooKeeper.delete("/zk002", -1);  
	        } catch (Exception e) {  
	            e.printStackTrace();  
	        }  
	    }  
	      
	    /** 
	     *  获取指定节点下的子节点 
	     * <br>------------------------------<br> 
	     */  
	    @Test  
	    public void testGetChild() {  
	         try {  
	             zooKeeper.create("/zk/001", "001".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
	             zooKeeper.create("/zk/002", "002".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
	               
	             List<String> list = zooKeeper.getChildren("/zk", true);  
	            for (String node : list) {  
	                LOGGER.info("node {}", node);  
	            }  
	        } catch (Exception e) {  
	             LOGGER.error(e.getMessage());  
	             Assert.fail();  
	        }  
	    }  
	    
	    @Test
	    public void tt(){
	    	  String ROOT = "/root-ktv";  
	    	  try{
	    		  // 创建一个与服务器的连接  
	  	        ZooKeeper zk = new ZooKeeper("localhost:2181,localhost:2182", 30000, new Watcher() {  
	  	            // 监控所有被触发的事件  
	  	            public void process(WatchedEvent event) {  
	  	                System.out.println("状态:" + event.getState()+":"+event.getType()+":"+event.getWrapper()+":"+event.getPath());  
	  	            }  
	  	        });  
	  	        Stat  s = zk.exists(ROOT, true);
	  	        if(s!=null){
	  	          List<String> ktvs = zk.getChildren(ROOT, true);  
		  	        System.out.println(Arrays.toString(ktvs.toArray()));  
		  	       for(String node : ktvs){  
		  	            // 删除节点  
		  	            zk.delete(ROOT+"/"+node,-1);  
		  	        }  
	  	        	 zk.delete(ROOT, -1);
	  	        }
	  	        // 创建一个总的目录ktv，并不控制权限，这里需要用持久化节点，不然下面的节点创建容易出错  
	  	        zk.create(ROOT, "root-ktv".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);  
	  	        
	  	        // 然后杭州开一个KTV ,       PERSISTENT_SEQUENTIAL 类型会自动加上 0000000000 自增的后缀  
	  	        zk.create(ROOT+"/杭州KTV", "杭州KTV".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT_SEQUENTIAL);  
	  	       
	  	        // 也可以在北京开一个,       EPHEMERAL session 过期了就会自动删除  
	  	        zk.create(ROOT+"/北京KTV", "北京KTV".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);  
	  	      
	  	        // 同理，我可以在北京开多个，EPHEMERAL_SEQUENTIAL  session 过期自动删除，也会加数字的后缀  
	  	        zk.create(ROOT+"/北京KTV-分店", "北京KTV-分店".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);  
	  	      
	  	        // 我们也可以 来看看 一共监视了多少家的ktv  
	  	        List<String> ktvs = zk.getChildren(ROOT, true);  
	  	        System.out.println(Arrays.toString(ktvs.toArray()));  
	  	       for(String node : ktvs){  
	  	            // 删除节点  
	  	    	   String s1 = new String(zk.getData(ROOT+"/"+node, true, null));
	  	    	   System.out.println(s1);
	  	    	   zk.setData(ROOT+"/"+node, (s1+" has Changed").getBytes(),-1);
	  	    	   s1 = new String(zk.getData(ROOT+"/"+node, true, null));
	  	    	   System.out.println(s1);
	  	            zk.delete(ROOT+"/"+node,-1);  
	  	        }  
	  	        // 根目录得最后删除的  
	  	        zk.delete(ROOT, -1);  
	  	        zk.close();  
	    	  }catch(Exception e){
	    		  e.printStackTrace();
	    	  }
	    	  
	    	
	    }
}
